use crate::{
    document::DocumentManager,
    diagnostics::DiagnosticsProvider,
    completion::CompletionProvider,
    hover::HoverProvider,
    definition::DefinitionProvider,
    references::ReferencesProvider,
    symbols::SymbolsProvider,
    formatting::FormattingProvider,
    LspConfig,
};
use codegraph_core::{GraphSystem, Result as CoreResult};
use tower_lsp::lsp_types::*;
use std::sync::Arc;
use std::time::{Duration, Instant};
use tokio::sync::{RwLock, Mutex};
use tower_lsp::{Client, LanguageServer};
use tracing::{debug, error, info};
use std::collections::HashMap;

/// Connection statistics for monitoring LSP performance
#[derive(Debug, Clone)]
pub struct ConnectionStats {
    pub requests_handled: u64,
    pub errors_count: u64,
    pub avg_response_time_ms: f64,
    pub last_activity: Option<Instant>,
    pub uptime: Duration,
    pub start_time: Instant,
}

impl Default for ConnectionStats {
    fn default() -> Self {
        Self {
            requests_handled: 0,
            errors_count: 0,
            avg_response_time_ms: 0.0,
            last_activity: None,
            uptime: Duration::default(),
            start_time: Instant::now(),
        }
    }
}

/// Main LSP backend that implements the Language Server Protocol
pub struct CodeGraphBackend {
    client: Client,
    config: Arc<RwLock<LspConfig>>,
    document_manager: Arc<DocumentManager>,
    graph_system: Arc<RwLock<Option<GraphSystem>>>,
    diagnostics_provider: Arc<DiagnosticsProvider>,
    completion_provider: Arc<CompletionProvider>,
    hover_provider: Arc<HoverProvider>,
    definition_provider: Arc<DefinitionProvider>,
    references_provider: Arc<ReferencesProvider>,
    symbols_provider: Arc<SymbolsProvider>,
    formatting_provider: Arc<FormattingProvider>,
    // Performance monitoring
    stats: Arc<Mutex<ConnectionStats>>,
    // Request timing for performance analysis
    request_times: Arc<Mutex<HashMap<String, Instant>>>,
}

impl CodeGraphBackend {
    pub fn new(client: Client) -> Self {
        let config = Arc::new(RwLock::new(LspConfig::default()));
        let document_manager = Arc::new(DocumentManager::new());
        let graph_system = Arc::new(RwLock::new(None));

        let diagnostics_provider = Arc::new(DiagnosticsProvider::new(
            client.clone(),
            document_manager.clone(),
            graph_system.clone(),
        ));

        let completion_provider = Arc::new(CompletionProvider::new(
            document_manager.clone(),
            graph_system.clone(),
        ));

        let hover_provider = Arc::new(HoverProvider::new(
            document_manager.clone(),
            graph_system.clone(),
        ));

        let definition_provider = Arc::new(DefinitionProvider::new(
            document_manager.clone(),
            graph_system.clone(),
        ));

        let references_provider = Arc::new(ReferencesProvider::new(
            document_manager.clone(),
            graph_system.clone(),
        ));

        let symbols_provider = Arc::new(SymbolsProvider::new(
            document_manager.clone(),
            graph_system.clone(),
        ));

        let formatting_provider = Arc::new(FormattingProvider::new(
            document_manager.clone(),
        ));

        let mut stats = ConnectionStats::default();
        stats.start_time = Instant::now();

        Self {
            client,
            config,
            document_manager,
            graph_system,
            diagnostics_provider,
            completion_provider,
            hover_provider,
            definition_provider,
            references_provider,
            symbols_provider,
            formatting_provider,
            stats: Arc::new(Mutex::new(stats)),
            request_times: Arc::new(Mutex::new(HashMap::new())),
        }
    }

    /// Initialize the graph system with proper error handling
    async fn initialize_graph_system(&self) -> CoreResult<()> {
        let mut graph_system = self.graph_system.write().await;
        if graph_system.is_none() {
            info!("Initializing CodeGraph system");
            match GraphSystem::new().await {
                Ok(system) => {
                    *graph_system = Some(system);
                    info!("CodeGraph system initialized successfully");
                    self.client
                        .log_message(MessageType::INFO, "CodeGraph system initialized")
                        .await;
                }
                Err(e) => {
                    error!("Failed to initialize CodeGraph system: {}", e);
                    self.client
                        .log_message(
                            MessageType::ERROR,
                            format!("Failed to initialize CodeGraph system: {}", e),
                        )
                        .await;
                    return Err(e);
                }
            }
        }
        Ok(())
    }

    /// Start timing a request for performance monitoring
    async fn start_request_timer(&self, request_id: String) {
        let mut request_times = self.request_times.lock().await;
        request_times.insert(request_id, Instant::now());
    }

    /// End timing a request and update statistics
    async fn end_request_timer(&self, request_id: &str, success: bool) {
        let mut request_times = self.request_times.lock().await;
        if let Some(start_time) = request_times.remove(request_id) {
            let duration = start_time.elapsed();
            let mut stats = self.stats.lock().await;

            stats.requests_handled += 1;
            if !success {
                stats.errors_count += 1;
            }

            // Update average response time using exponential moving average
            let duration_ms = duration.as_millis() as f64;
            if stats.avg_response_time_ms == 0.0 {
                stats.avg_response_time_ms = duration_ms;
            } else {
                stats.avg_response_time_ms = 0.9 * stats.avg_response_time_ms + 0.1 * duration_ms;
            }

            stats.last_activity = Some(Instant::now());
            stats.uptime = stats.start_time.elapsed();

            debug!(
                "Request {} completed in {}ms (success: {})",
                request_id,
                duration_ms,
                success
            );
        }
    }

    /// Get current connection statistics
    pub async fn get_stats(&self) -> ConnectionStats {
        let stats = self.stats.lock().await;
        stats.clone()
    }

    async fn get_language_from_uri(&self, uri: &Url) -> Option<String> {
        if let Some(path) = uri.to_file_path().ok() {
            if let Some(extension) = path.extension() {
                if let Some(ext_str) = extension.to_str() {
                    return match ext_str {
                        "py" => Some("python".to_string()),
                        "js" => Some("javascript".to_string()),
                        "ts" => Some("typescript".to_string()),
                        "rs" => Some("rust".to_string()),
                        "java" => Some("java".to_string()),
                        "cpp" | "cc" | "cxx" => Some("cpp".to_string()),
                        "c" => Some("c".to_string()),
                        "go" => Some("go".to_string()),
                        _ => None,
                    };
                }
            }
        }
        None
    }
}

#[tower_lsp::async_trait]
impl LanguageServer for CodeGraphBackend {
    async fn initialize(&self, _params: InitializeParams) -> tower_lsp::jsonrpc::Result<InitializeResult> {
        info!("LSP Initialize request received");
        
        // Initialize the graph system
        if let Err(e) = self.initialize_graph_system().await {
            error!("Failed to initialize graph system: {}", e);
        }

        let server_capabilities = ServerCapabilities {
            text_document_sync: Some(TextDocumentSyncCapability::Kind(
                TextDocumentSyncKind::INCREMENTAL,
            )),
            completion_provider: Some(CompletionOptions {
                resolve_provider: Some(true),
                trigger_characters: Some(vec![".".to_string(), "::".to_string()]),
                work_done_progress_options: WorkDoneProgressOptions::default(),
                completion_item: None,
                all_commit_characters: None,
            }),
            hover_provider: Some(HoverProviderCapability::Simple(true)),
            definition_provider: Some(OneOf::Left(true)),
            references_provider: Some(OneOf::Left(true)),
            document_symbol_provider: Some(OneOf::Left(true)),
            workspace_symbol_provider: Some(OneOf::Left(true)),
            code_action_provider: Some(CodeActionProviderCapability::Simple(true)),
            document_formatting_provider: Some(OneOf::Left(true)),
            document_range_formatting_provider: Some(OneOf::Left(true)),
            rename_provider: Some(OneOf::Left(true)),
            workspace: Some(WorkspaceServerCapabilities {
                workspace_folders: Some(WorkspaceFoldersServerCapabilities {
                    supported: Some(true),
                    change_notifications: Some(OneOf::Left(true)),
                }),
                file_operations: None,
            }),
            ..Default::default()
        };

        Ok(InitializeResult {
            capabilities: server_capabilities,
            server_info: Some(ServerInfo {
                name: "CodeGraph-RT LSP".to_string(),
                version: Some("0.1.0".to_string()),
            }),
        })
    }

    async fn initialized(&self, _: InitializedParams) {
        info!("LSP server initialized");
        self.client
            .log_message(MessageType::INFO, "CodeGraph-RT LSP server initialized")
            .await;
    }

    async fn shutdown(&self) -> tower_lsp::jsonrpc::Result<()> {
        info!("LSP server shutting down");
        Ok(())
    }

    async fn did_open(&self, params: DidOpenTextDocumentParams) {
        info!("Document opened: {}", params.text_document.uri);
        
        let language = self.get_language_from_uri(&params.text_document.uri).await;
        
        self.document_manager.open_document(
            params.text_document.uri.clone(),
            params.text_document.text,
            language,
        ).await;

        // Trigger diagnostics
        if let Err(e) = self.diagnostics_provider.update_diagnostics(&params.text_document.uri).await {
            error!("Failed to update diagnostics: {}", e);
        }
    }

    async fn did_change(&self, params: DidChangeTextDocumentParams) {
        if let Err(e) = self.document_manager.update_document(
            &params.text_document.uri,
            params.content_changes,
        ).await {
            error!("Failed to update document: {}", e);
            return;
        }

        // Trigger diagnostics with a delay
        if let Err(e) = self.diagnostics_provider.update_diagnostics(&params.text_document.uri).await {
            error!("Failed to update diagnostics: {}", e);
        }
    }

    async fn did_close(&self, params: DidCloseTextDocumentParams) {
        info!("Document closed: {}", params.text_document.uri);
        self.document_manager.close_document(&params.text_document.uri).await;
        
        // Clear diagnostics
        self.client
            .publish_diagnostics(params.text_document.uri, vec![], None)
            .await;
    }

    async fn completion(&self, params: CompletionParams) -> tower_lsp::jsonrpc::Result<Option<CompletionResponse>> {
        let request_id = format!("completion_{}", uuid::Uuid::new_v4());
        self.start_request_timer(request_id.clone()).await;

        let config = self.config.read().await;
        if !config.enable_completion {
            self.end_request_timer(&request_id, true).await;
            return Ok(None);
        }

        let result = match self.completion_provider.provide_completion(&params).await {
            Ok(completion) => {
                debug!(
                    "Completion provided for {} at {:?}",
                    params.text_document_position.text_document.uri,
                    params.text_document_position.position
                );
                self.end_request_timer(&request_id, true).await;
                Ok(completion)
            }
            Err(e) => {
                error!("Completion error: {}", e);
                self.end_request_timer(&request_id, false).await;
                Ok(None)
            }
        };

        result
    }

    async fn hover(&self, params: HoverParams) -> tower_lsp::jsonrpc::Result<Option<Hover>> {
        let request_id = format!("hover_{}", uuid::Uuid::new_v4());
        self.start_request_timer(request_id.clone()).await;

        let config = self.config.read().await;
        if !config.enable_hover {
            self.end_request_timer(&request_id, true).await;
            return Ok(None);
        }

        let result = match self.hover_provider.provide_hover(&params).await {
            Ok(hover) => {
                debug!(
                    "Hover provided for {} at {:?}",
                    params.text_document_position_params.text_document.uri,
                    params.text_document_position_params.position
                );
                self.end_request_timer(&request_id, true).await;
                Ok(hover)
            }
            Err(e) => {
                error!("Hover error: {}", e);
                self.end_request_timer(&request_id, false).await;
                Ok(None)
            }
        };

        result
    }

    async fn goto_definition(&self, params: GotoDefinitionParams) -> tower_lsp::jsonrpc::Result<Option<GotoDefinitionResponse>> {
        let request_id = format!("definition_{}", uuid::Uuid::new_v4());
        self.start_request_timer(request_id.clone()).await;

        let config = self.config.read().await;
        if !config.enable_definition {
            self.end_request_timer(&request_id, true).await;
            return Ok(None);
        }

        let result = match self.definition_provider.provide_definition(&params).await {
            Ok(definition) => {
                debug!(
                    "Definition provided for {} at {:?}",
                    params.text_document_position_params.text_document.uri,
                    params.text_document_position_params.position
                );
                self.end_request_timer(&request_id, true).await;
                Ok(definition)
            }
            Err(e) => {
                error!("Definition error: {}", e);
                self.end_request_timer(&request_id, false).await;
                Ok(None)
            }
        };

        result
    }

    async fn references(&self, params: ReferenceParams) -> tower_lsp::jsonrpc::Result<Option<Vec<Location>>> {
        let request_id = format!("references_{}", uuid::Uuid::new_v4());
        self.start_request_timer(request_id.clone()).await;

        let config = self.config.read().await;
        if !config.enable_references {
            self.end_request_timer(&request_id, true).await;
            return Ok(None);
        }

        let result = match self.references_provider.provide_references(&params).await {
            Ok(references) => {
                debug!(
                    "References provided for {} at {:?}",
                    params.text_document_position.text_document.uri,
                    params.text_document_position.position
                );
                self.end_request_timer(&request_id, true).await;
                Ok(references)
            }
            Err(e) => {
                error!("References error: {}", e);
                self.end_request_timer(&request_id, false).await;
                Ok(None)
            }
        };

        result
    }

    async fn document_symbol(&self, params: DocumentSymbolParams) -> tower_lsp::jsonrpc::Result<Option<DocumentSymbolResponse>> {
        let config = self.config.read().await;
        if !config.enable_symbols {
            return Ok(None);
        }

        match self.symbols_provider.provide_document_symbols(&params).await {
            Ok(symbols) => Ok(symbols),
            Err(e) => {
                error!("Document symbols error: {}", e);
                Ok(None)
            }
        }
    }

    async fn formatting(&self, params: tower_lsp::lsp_types::DocumentFormattingParams) -> tower_lsp::jsonrpc::Result<Option<Vec<tower_lsp::lsp_types::TextEdit>>> {
        let request_id = format!("formatting_{}", uuid::Uuid::new_v4());
        self.start_request_timer(request_id.clone()).await;

        let config = self.config.read().await;
        if !config.enable_formatting {
            self.end_request_timer(&request_id, true).await;
            return Ok(None);
        }

        let result = match self.formatting_provider.format_document(&params).await {
            Ok(edits) => {
                debug!(
                    "Formatting provided for {}",
                    params.text_document.uri
                );
                self.end_request_timer(&request_id, true).await;
                Ok(edits)
            }
            Err(e) => {
                error!("Formatting error: {}", e);
                self.end_request_timer(&request_id, false).await;
                Ok(None)
            }
        };

        result
    }

    async fn range_formatting(&self, params: tower_lsp::lsp_types::DocumentRangeFormattingParams) -> tower_lsp::jsonrpc::Result<Option<Vec<tower_lsp::lsp_types::TextEdit>>> {
        let request_id = format!("range_formatting_{}", uuid::Uuid::new_v4());
        self.start_request_timer(request_id.clone()).await;

        let config = self.config.read().await;
        if !config.enable_formatting {
            self.end_request_timer(&request_id, true).await;
            return Ok(None);
        }

        let result = match self.formatting_provider.format_range(&params).await {
            Ok(edits) => {
                debug!(
                    "Range formatting provided for {} at {:?}",
                    params.text_document.uri,
                    params.range
                );
                self.end_request_timer(&request_id, true).await;
                Ok(edits)
            }
            Err(e) => {
                error!("Range formatting error: {}", e);
                self.end_request_timer(&request_id, false).await;
                Ok(None)
            }
        };

        result
    }

    async fn code_action(&self, params: tower_lsp::lsp_types::CodeActionParams) -> tower_lsp::jsonrpc::Result<Option<tower_lsp::lsp_types::CodeActionResponse>> {
        let request_id = format!("code_action_{}", uuid::Uuid::new_v4());
        self.start_request_timer(request_id.clone()).await;

        let config = self.config.read().await;
        if !config.enable_code_actions {
            self.end_request_timer(&request_id, true).await;
            return Ok(None);
        }

        let result = match self.formatting_provider.provide_code_actions(&params).await {
            Ok(actions) => {
                debug!(
                    "Code actions provided for {} at {:?}",
                    params.text_document.uri,
                    params.range
                );
                self.end_request_timer(&request_id, true).await;
                Ok(actions)
            }
            Err(e) => {
                error!("Code action error: {}", e);
                self.end_request_timer(&request_id, false).await;
                Ok(None)
            }
        };

        result
    }
}
